﻿using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Threading;
using Less.Tools;

namespace Less.Core.Loggers
{
    /// <summary>
    /// Logger that writes entries to a file
    /// Works asynchronously
    /// </summary>
    public class FileLogger : BaseLogger
    {
        private string _directoryName;
        private String _fileName;
        private StreamWriter _writer;

        public static string LogsDirectory
        {
            get
            {
                string name = ConfigurationManager.AppSettings["FileLogger.LogsDirectory"];
                if (name == null) name = @"\\Data\\Logs\\";
                return name;
            }
        }

        public static int MaxLogSizeKb
        {
            get
            {
                string name = ConfigurationManager.AppSettings["FileLogger.MaxLogSizeKb"];
                if (name == null) name = "1024";
                int number = 1024;
                int.TryParse(name, out number);
                return number;
            }
        }


        /// <summary>
        /// The writer used to access the file
        /// </summary>
        public StreamWriter Writer
        {
            get
            {
                if (MaxLogSizeKb > 0 && LastSize >= MaxLogSizeKb * 1024)
                {
                    if (this._writer != null)
                    {
                        this._writer.Close();
                    }
                    File.Move(FileName, FileBackupName);
                    LastSize = 0;
                }
                var fileStream = new FileStream(FileName, FileMode.Append);
                this._writer = new StreamWriter(fileStream); 
                return this._writer;
            }
            set { this._writer = value; }
        }

        /// <summary>
        /// The name of the file to which this Logger is writing.
        /// </summary>
        private string FileName
        {
            get { return this._fileName ?? (this._fileName = string.Format("{0}{1}_{2}{3}", DirectoryName, Config.ApplicationName, TimeStamp, ".log")); }
        }

        /// <summary>
        /// Name of the Directory where we will create files
        /// </summary>
        private string DirectoryName
        {
            get
            {
                if (this._directoryName == null)
                {
                    string logDirectory = LogsDirectory;
                    // Note : Check if is LogsDirectory is relative (default) or absolute
                    if (logDirectory.Left(3) != Config.WorkDirectory.Left(3))
                    {
                        this._directoryName = Config.WorkDirectory + logDirectory;
                    }
                    else
                    {
                        this._directoryName = logDirectory;
                    }
                    if (this._directoryName != null && !Directory.Exists(this._directoryName))
                    {
                        Directory.CreateDirectory(this._directoryName);
                    }
                }
                return this._directoryName;
            }
        }

        /// <summary>
        /// Date part for the loging file name
        /// </summary>
        private static string TimeStamp { get { return DateTime.Now.ToString("yyyyMMdd"); } }

        /// <summary>
        /// Creates new name based on the current time (second)
        /// </summary>
        /// <value></value>
        private string FileBackupName { get { return DirectoryName + Path.GetFileNameWithoutExtension(FileName) + "." + DateTime.Now.ToString("HHmmss") + ".log"; } }

        /// <summary>
        /// current file size
        /// </summary>
        public static long LastSize { get; private set; }

        /// <summary>
        /// Write the message to the file asynchronously.
        /// </summary>
        /// <param name="message">The String representing the LogEntry being logged.</param>
        /// <returns></returns>
        public override bool WriteMessage(String message)
        {
            var write = ThreadPool.QueueUserWorkItem(DoWriteMessage, message);
            if (!write)
            {
                Trace.WriteLine("**************FAILDoWrite");
            }
            return true;
        }

        /// <summary>
        /// Writes the message to the file
        /// </summary>
        /// <param name="message"></param>
        private void DoWriteMessage(object message)
        {
            // We need to lock the execution to prevent multiple threads working on the same file
            lock (this)
            {
                using (StreamWriter writer = this.Writer)
                {
                    if (writer != null)
                    {
                        writer.WriteLine(message);
                        long size = new FileInfo(FileName).Length;
                        LastSize = size ;
                    }
                    else
                    {
                        Trace.WriteLine("**** NOT File Writted: " + message);
                    }
                }
            }
        }

        /// <summary>
        /// Formatter to generate notepad++ smalltalk tags to have a colorized log ;-)
        /// </summary>
        /// <param name="logSeverity"></param>
        /// <returns></returns>
        public override string FormatSeverity(EntrySeverities logSeverity)
        {
            switch (logSeverity)
            {
                case EntrySeverities.Debug:
                    return "'dbg'".FixedLenght(7);
                case EntrySeverities.Info:
                    return "inf:".FixedLenght(7);
                case EntrySeverities.Warning:
                    return "#Wrng".FixedLenght(7);
                case EntrySeverities.Metric:
                    return "Mtrc:".FixedLenght(7);
                case EntrySeverities.Error:
                    return "\"Error\"".FixedLenght(7);
                case EntrySeverities.Fatal:
                    return "\"FATAL\"".FixedLenght(7);
                default:
                    return "";
            }
        }

        public override string FormatCategory(EntryCategories logCategory)
        {
            return base.FormatCategory(logCategory).FixedLenght(8);
        }

        //public void Dispose()
        //{
        //    Dispose(true);
        //}
        //protected virtual void Dispose(bool nativeAndManaged)
        //{
        //    //if (this._writer != null)
        //        //this._writer.Dispose();
        //}
    }
}
